﻿namespace ExcelFileMonitor.NPOI.Utilities
{
    public class FileAccessHelper
    {
        private readonly int _maxRetryAttempts;
        private readonly int _retryDelayMs;

        public FileAccessHelper(int maxRetryAttempts = 3, int retryDelayMs = 1000)
        {
            _maxRetryAttempts = maxRetryAttempts;
            _retryDelayMs = retryDelayMs;
        }

        public async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> operation, string filePath)
        {
            if (operation == null)
                throw new ArgumentNullException(nameof(operation));

            Exception lastException = null;

            for (int attempt = 1; attempt <= _maxRetryAttempts; attempt++)
            {
                try
                {
                    return await operation().ConfigureAwait(false);
                }
                catch (IOException ex) when (IsFileLocked(ex) && attempt < _maxRetryAttempts)
                {
                    lastException = ex;
                    await Task.Delay(_retryDelayMs).ConfigureAwait(false);
                }
                catch (IOException ex) when (IsFileLocked(ex))
                {
                    lastException = ex;
                    break;
                }
            }

            throw new IOException($"文件被锁定，重试{_maxRetryAttempts}次后仍无法访问: {filePath}", lastException);
        }

        public async Task ExecuteWithRetryAsync(Func<Task> operation, string filePath)
        {
            if (operation == null)
                throw new ArgumentNullException(nameof(operation));

            await ExecuteWithRetryAsync(async () =>
            {
                await operation().ConfigureAwait(false);
                return true;
            }, filePath).ConfigureAwait(false);
        }

        private bool IsFileLocked(IOException ex)
        {
            if (ex == null) return false;

            int errorCode = System.Runtime.InteropServices.Marshal.GetHRForException(ex) & 0xFFFF;

            return errorCode == 32 || errorCode == 33 ||
                   ex.Message.Contains("正由另一进程使用", StringComparison.OrdinalIgnoreCase) ||
                   ex.Message.Contains("being used by another process", StringComparison.OrdinalIgnoreCase) ||
                   ex.Message.Contains("The process cannot access the file", StringComparison.OrdinalIgnoreCase);
        }

        public static bool IsFileLocked(string filePath)
        {
            if (!File.Exists(filePath))
                return false;

            try
            {
                using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                {
                    return false;
                }
            }
            catch (IOException)
            {
                return true;
            }
        }
    }
}
